
#include "PthreadPool.h"



void mmapTransfileGet(int fd,int sockfd,int filesize){
    ftruncate(fd,filesize);
    char * p = (char *)mmap(NULL,filesize,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    send(sockfd,p,filesize,MSG_NOSIGNAL);
    munmap(p,filesize);
    return ;
}

void trainTransfileGets(int fd,int sockfd){
    Train train;
    char buf[1024];
    while(1){
        bzero(buf,sizeof(buf));
        int rret = read(fd,buf,sizeof(buf));
        if(rret == 0){
            train.length = 0;
            send(sockfd,&train,sizeof(train.length)+train.length,MSG_NOSIGNAL);
            break;
        }
        train.length = rret;
        memcpy(train.data,buf,train.length);
        send(sockfd,&train,sizeof(train.length)+train.length,MSG_NOSIGNAL);
    }
    return ;
}


void getsDispose(int netfd,char * prev_path){
    int epollfd = epoll_create(netfd);
    struct epoll_event event;
    event.events = EPOLLIN;
    event.data.fd = netfd;
    epoll_ctl(epollfd,EPOLL_CTL_ADD,netfd,&event);
    struct epoll_event readyset[5];
    epoll_wait(epollfd,readyset,5,-1);

    Train train;
    recv(readyset[0].data.fd,&train.length,sizeof(train.length),MSG_WAITALL);  // 接收路径   /fsd/fdsf/fs
    recv(readyset[0].data.fd,train.data,train.length,MSG_WAITALL);
    char p[1024];
    bzero(p,sizeof(p));
    memcpy(p,train.data,train.length);
    
    char prev_path_cp[1024] = {0};
    memcpy(prev_path_cp,prev_path,strlen(prev_path));

    char filepath[1024] = {0};      //拼出真实文件路径
    strcat(filepath,prev_path_cp);
    strcat(filepath,p);
    printf("真实文件路径为:%s\n",filepath);

    int has = access(filepath,F_OK);            //检查有没有这个文件
    if(has != 0){
        char c = '0';
        send(readyset[0].data.fd,&c,1,MSG_NOSIGNAL);
    }
    if(has == 0){
        DIR * xxx = opendir(filepath);
        if(xxx != NULL){
            printf("无法传输目录文件!\n");
            char c = '0';
            send(readyset[0].data.fd,&c,1,MSG_NOSIGNAL);
            return ;
        }
        char c = '1';
        send(readyset[0].data.fd,&c,1,MSG_NOSIGNAL);
        int fd = open(filepath,O_RDWR);
        struct stat statbuf;
        fstat(fd,&statbuf);
        char isBig ;
        if(statbuf.st_size > 104857600){
            isBig = '1';
        }else{
            isBig = '0';
        }
        send(readyset[0].data.fd,&isBig,1,MSG_NOSIGNAL);
        train.length = sizeof(statbuf.st_size);
        memcpy(train.data,&statbuf.st_size,train.length);
        send(readyset[0].data.fd,&train,sizeof(train.length)+train.length,MSG_NOSIGNAL);
        if(isBig == '1'){
            mmapTransfileGet(fd,readyset[0].data.fd,statbuf.st_size);
        }
        if(isBig == '0'){
            trainTransfileGets(fd,readyset[0].data.fd);
        }

    }    
    return ;
}



void trainTransfilePuts(int fd,int sockfd,long sfilesize,long cfilesize){
    printf("transfile_enter\n");
    if(sfilesize == 0){              //如果文件是错的，那么就截断或拉长。防止服务器文件比客户机文件还大的情况。
        ftruncate(fd,cfilesize);
    }else{
        printf("断点重传!\n");
    }
    lseek(fd,sfilesize,SEEK_SET);
    Train train;
    while(1){
        int rret1 = recv(sockfd,&train.length,sizeof(train.length),MSG_WAITALL);
        if(rret1 == 0){
            printf("传输中断\n");
            break;
        }
        if(train.length == 0){
            printf("transfile over\n");
            break;
        }
        int rret2 = recv(sockfd,train.data,train.length,MSG_WAITALL);
        if(rret2 == 0){
            printf("传输中断\n");
            break;
        }
        write(fd,train.data,train.length);
    }
    return;  
}


void mmapTransfilePuts(int fd,int sockfd,long sfilesize,long cfilesize){
    printf("into mmap\n");

    char buf[4096] ;
    lseek(fd,sfilesize,SEEK_SET);
    ftruncate(fd,sfilesize);
    if(sfilesize!=0){
        printf("断点重传!\n");
    }
    long readedsize = sfilesize; 
    long remainsize = 0;
    int rret = 0;
    while(1){
        bzero(buf,sizeof(buf));
        remainsize = cfilesize - readedsize;
       if(remainsize > sizeof(buf)){
            rret = recv(sockfd,buf,sizeof(buf),MSG_WAITALL);
       }else{
            rret = recv(sockfd,buf,remainsize,MSG_WAITALL);
            write(fd,buf,rret);
            break;
       }
       if(rret == 0){
            printf("接收中断！\n");
            break;
       }
       write(fd,buf,rret);
       readedsize = readedsize + rret;
    }
   
    printf("out map\n");
    return ;
}



void putsDispose(int netfd,char * prev_path){
    int epollfd = epoll_create(1);
    struct epoll_event event;
    event.events = EPOLLIN;
    event.data.fd = netfd;
    epoll_ctl(epollfd,EPOLL_CTL_ADD,netfd,&event);
    struct epoll_event readyset[5];
    epoll_wait(epollfd,readyset,5,-1);

    Train train;
    recv(readyset[0].data.fd,&train.length,sizeof(train.length),MSG_WAITALL);
    recv(readyset[0].data.fd,train.data,train.length,MSG_WAITALL);
    char s_curpath[1024] = {0};            //接收客户端显示的“当前工作目录”
    memcpy(s_curpath,train.data,train.length);
    printf("s_curpath:%s\n",s_curpath);         

    char prev_path_cp[1024] = {0};
    memcpy(prev_path_cp,prev_path,strlen(prev_path));

    char cur_workpath[1024] = {0};
    strcat(cur_workpath,prev_path_cp);
    if(strcmp(s_curpath,"/")!=0){
        strcat(cur_workpath,s_curpath);       //拼出真实的当前工作目录
    }
    printf("当前真实工作目录为:%s\n",cur_workpath);

    recv(readyset[0].data.fd,&train.length,sizeof(train.length),MSG_WAITALL);   //接收文件名字
    recv(readyset[0].data.fd,train.data,train.length,MSG_WAITALL);                                                                           
    char filename[128] = {0};
    memcpy(filename,train.data,train.length);
    printf("filename:%s\n",filename);

    recv(readyset[0].data.fd,&train.length,sizeof(train.length),MSG_WAITALL);   //接收客户端文件大小
    recv(readyset[0].data.fd,train.data,train.length,MSG_WAITALL);
    long filesize = 0; 
    memcpy(&filesize,train.data,train.length);
    printf("收到client filesize:%ld\n",filesize);
    
    char isBig;                                             // 接收是否是大文件
    recv(readyset[0].data.fd,&isBig,1,MSG_WAITALL);    
    printf("isBig:%c\n",isBig);
    
    char ass_filename[128] = {0};        //strcat 会改掉第二个参数，所以我们搞个文件名副本
    memcpy(ass_filename,filename,strlen(filename));
    char ass_xiegang[] = "/";
    strcat(cur_workpath,ass_xiegang);
    strcat(cur_workpath,ass_filename);      //拼接文件名，得到文件真实地址
    printf("filepath:%s\n",cur_workpath);
    printf("filename:%s\n",filename);
    int rret = access(cur_workpath,F_OK);                       //判断一下有没有
    printf("ret:%d\n",rret);
    char buf[64] = {0};
    if(rret != 0){
        send(readyset[0].data.fd,"0",1,MSG_NOSIGNAL);
        epoll_wait(epollfd,readyset,5,-1);
        int fd = open(cur_workpath,O_RDWR|O_CREAT|O_TRUNC,0666);    //在当前文件夹下创建文件
        if(isBig == '0'){
            trainTransfilePuts(fd,readyset[0].data.fd,0,filesize);
        }
        if(isBig == '1'){
            mmapTransfilePuts(fd,readyset[0].data.fd,0,filesize);
        }
    }
    if(rret == 0){                                      //有
        send(readyset[0].data.fd,"1",1,MSG_NOSIGNAL);     // 发送“有”信号
        int fd = open(cur_workpath,O_RDWR);             //打开文件
        printf("打开成功:fd=%d\n",fd);
        struct stat statbuf;                        //发送服务端文件长度
        fstat(fd,&statbuf);
        printf("fstat seccessful!\n");
        train.length = sizeof(statbuf.st_size);
        memcpy(train.data,&statbuf.st_size,train.length);
        send(readyset[0].data.fd,&train,sizeof(train.length)+train.length,MSG_NOSIGNAL);
        //  char md5_str[MD5_STR_LEN + 1];        
        //  Compute_file_md5(filename,md5_str);
        MD5_CTX ctx;                            //计算并发送md5
        MD5_Init(&ctx);
        char buff[4096];
        while(1){
            bzero(buff,sizeof(buff));
            ssize_t rret = read(fd,buff,sizeof(buff));
            if(rret == 0){
                break;
            }              
            MD5_Update(&ctx,buff,rret);
        }
        unsigned char md[16];
        MD5_Final(md,&ctx);
        char md5_str_s[33] = {0};
        char num[3] = {0};
        for(int k =0 ; k < 16 ; ++k){
            sprintf(num,"%02x",md[k]);
            strcat(md5_str_s,num);
        }

        printf("compute md5 seccessful!\n");
        printf("server md5:%s\n",md5_str_s);
        train.length = sizeof(md5_str_s);
        memcpy(train.data,md5_str_s,train.length);
        send(readyset[0].data.fd,&train,sizeof(train.length)+train.length,MSG_NOSIGNAL);


        epoll_wait(epollfd,readyset,5,-1);    //等客户端确认文件是否相同和是否需要重传。01 不一样且需要重传，11  一样且需要续传, 10一样不需要重传
        bzero(buf,sizeof(buf));            
        recv(readyset[0].data.fd,buf,2,MSG_WAITALL);
        if(strcmp(buf,"10") == 0){
            printf("收到客户端:10\n");
            return ;
        }else if(strcmp(buf,"11") == 0){
            if(isBig == '1'){
                printf("收到客户端:11\n");
                printf("进入mmap\n");
                mmapTransfilePuts(fd,readyset[0].data.fd,statbuf.st_size,filesize);   //文件描述符，套接字，服务器文件大小，客户机文件大小
            }
            if(isBig == '0'){
                printf("收到客户端:11\n");
                printf("进入train\n");
                trainTransfilePuts(fd,readyset[0].data.fd,statbuf.st_size,filesize);    // 文件描述符，套接字，服务器文件大小,客户机文件大小
            }
        }else if(strcmp(buf,"01") == 0){
            if(isBig == '1'){
                printf("收到客户端:01\n");
                printf("进入mmap\n");
               mmapTransfilePuts(fd,readyset[0].data.fd,0,filesize);    
            }
            if(isBig == '0'){
                printf("收到客户端:01\n");
                printf("进入train\n");
               trainTransfilePuts(fd,readyset[0].data.fd,0,filesize);
            }
        }
    }
    return ;
}

void mkdirDispose(int netfd,char * prev_path){
    int epollfd = epoll_create(netfd);
    struct epoll_event event;
    event.events = EPOLLIN;
    event.data.fd = netfd;
    epoll_ctl(epollfd,EPOLL_CTL_ADD,netfd,&event);
    struct epoll_event readyset[5];
    epoll_wait(epollfd,readyset,5,-1);

    Train train;
    recv(readyset[0].data.fd,&train.length,sizeof(train.length),MSG_WAITALL);  // 接收路径   /fsd/fdsf/fs
    recv(readyset[0].data.fd,train.data,train.length,MSG_WAITALL);
    char p[1024];
    bzero(p,sizeof(p));
    memcpy(p,train.data,train.length);
    
    char prev_path_cp[1024] = {0};
    memcpy(prev_path_cp,prev_path,strlen(prev_path));

    char filepath[1024] = {0};      //拼出真实文件路径
    strcat(filepath,prev_path_cp);
    strcat(filepath,p);
    printf("真实文件路径为:%s\n",filepath);
    int rret = mkdir(filepath,0777);
    if(rret != 0){
        perror("mkdir");
        char c = '0';
        send(readyset[0].data.fd,&c,1,MSG_NOSIGNAL);
        return ;
    }
    char c = '1';
    send(readyset[0].data.fd,&c,1,MSG_NOSIGNAL);
    printf("发送成功！\n");
    return ;
}



void rmdirDispose(int netfd,char * prev_path){
    int epollfd = epoll_create(netfd);
    struct epoll_event event;
    event.events = EPOLLIN;
    event.data.fd = netfd;
    epoll_ctl(epollfd,EPOLL_CTL_ADD,netfd,&event);
    struct epoll_event readyset[5];
    epoll_wait(epollfd,readyset,5,-1);

    Train train;
    recv(readyset[0].data.fd,&train.length,sizeof(train.length),MSG_WAITALL);  // 接收路径   /fsd/fdsf/fs
    recv(readyset[0].data.fd,train.data,train.length,MSG_WAITALL);
    char p[1024];
    bzero(p,sizeof(p));
    memcpy(p,train.data,train.length);
    
    char prev_path_cp[1024] = {0};
    memcpy(prev_path_cp,prev_path,strlen(prev_path));

    char filepath[1024] = {0};      //拼出真实文件路径
    strcat(filepath,prev_path_cp);
    strcat(filepath,p);
    printf("真实文件路径为:%s\n",filepath);
    int rret = rmdir(filepath);
    if(rret != 0){
        perror("rmdir");
        char c = '0';
        send(readyset[0].data.fd,&c,1,MSG_NOSIGNAL);
        return ;
    }
    char c = '1';
    send(readyset[0].data.fd,&c,1,MSG_NOSIGNAL);
    printf("发送成功！\n");

    return ; 
}
void  rmfileDispose(int netfd,char * prev_path){

    int epollfd = epoll_create(netfd);
    struct epoll_event event;
    event.events = EPOLLIN;
    event.data.fd = netfd;
    epoll_ctl(epollfd,EPOLL_CTL_ADD,netfd,&event);
    struct epoll_event readyset[5];
    epoll_wait(epollfd,readyset,5,-1);

    Train train;
    recv(readyset[0].data.fd,&train.length,sizeof(train.length),MSG_WAITALL);  // 接收路径   /fsd/fdsf/fs
    recv(readyset[0].data.fd,train.data,train.length,MSG_WAITALL);
    char p[1024];
    bzero(p,sizeof(p));
    memcpy(p,train.data,train.length);
    
    char prev_path_cp[1024] = {0};
    memcpy(prev_path_cp,prev_path,strlen(prev_path));

    char filepath[1024] = {0};      //拼出真实文件路径
    strcat(filepath,prev_path_cp);
    strcat(filepath,p);
    printf("真实文件路径为:%s\n",filepath);
    int rret = unlink(filepath);
    if(rret != 0){
        perror("rmfile");
        char c = '0';
        send(readyset[0].data.fd,&c,1,MSG_NOSIGNAL);
        return ;
    }
    char c = '1';
    send(readyset[0].data.fd,&c,1,MSG_NOSIGNAL);
    printf("发送成功！\n");

    return ; 

}

void  lsDispose(int netfd,char * prev_path){
    
    int epollfd = epoll_create(netfd);
    struct epoll_event event;
    event.events = EPOLLIN;
    event.data.fd = netfd;
    epoll_ctl(epollfd,EPOLL_CTL_ADD,netfd,&event);
    struct epoll_event readyset[5];
    epoll_wait(epollfd,readyset,5,-1);

    Train train;
    recv(readyset[0].data.fd,&train.length,sizeof(train.length),MSG_WAITALL);  // 接收路径   /fsd/fdsf/fs
    recv(readyset[0].data.fd,train.data,train.length,MSG_WAITALL);
    char p[1024];
    bzero(p,sizeof(p));
    memcpy(p,train.data,train.length);
    
    char prev_path_cp[1024] = {0};
    memcpy(prev_path_cp,prev_path,strlen(prev_path));

    char filepath[1024] = {0};      //拼出真实目录文件路径
    strcat(filepath,prev_path_cp);
    strcat(filepath,p);
    printf("真实目录文件路径为:%s\n",filepath);
    
    DIR *dirp = opendir(filepath);
    struct dirent * pdirent = (struct dirent *)calloc(1,sizeof(struct dirent));
    while(1){
        pdirent = readdir(dirp);
        if(pdirent == NULL){
            train.length = 0;
            send(readyset[0].data.fd,&train,sizeof(train.length)+train.length,MSG_NOSIGNAL) ;
            break;
        }
        train.length = sizeof(pdirent->d_name);
        memcpy(train.data,&pdirent->d_name,train.length); 
        send(readyset[0].data.fd,&train,sizeof(train.length)+train.length,MSG_NOSIGNAL);
        send(readyset[0].data.fd,&pdirent->d_type,sizeof(unsigned char),MSG_NOSIGNAL);
    }
    
    return ;
}



void cdDispose(int netfd,char * prev_path){
    printf("into cdDispose\n");

    char path[1024] = {0};
    strncpy(path,prev_path,strlen(prev_path));

    int epollfd = epoll_create(1);
    struct epoll_event event;
    event.events = EPOLLIN;
    event.data.fd = netfd;
    epoll_ctl(epollfd,EPOLL_CTL_ADD,netfd,&event);
    struct epoll_event readyset[5];
    epoll_wait(epollfd,readyset,5,-1);
    Train train;                               //接收路径    /sfas/fsadf/sdfas/dsfsad/fs
    recv(readyset[0].data.fd,&train.length,sizeof(train.length),MSG_WAITALL);
    recv(readyset[0].data.fd,train.data,train.length,MSG_WAITALL);
    char rear_path[1024] = {0};
    memcpy(rear_path,train.data,train.length);
    
    strcat(path,rear_path);      //拼接出完整路径  ../netdisk/username/fsd/fsdf/fsdf/fsd
    
    DIR * dir = opendir(path);
    char has;
    if(dir == NULL){
        has = '0';
        send(readyset[0].data.fd,&has,1,MSG_NOSIGNAL);
    }else{
        has = '1';
        send(readyset[0].data.fd,&has,1,MSG_NOSIGNAL);
        closedir(dir);
    }

    printf("out cdDispose\n");
    return ;
}



int login(const char *key,const char *usrname,char * uname){
    //printf("usrname=%s,key=%s\n",usrname,key);
    char *name = (char*)calloc(strlen(usrname),sizeof(char));
    strncpy(name,usrname,strlen(usrname));
    //printf("name=%s\n",name);
    struct spwd *pspwd = getspnam(name);//如果用户名错误会直接
    if(pspwd == NULL){
        return -1;
    }
    //printf("pspwd.key=%s",pspwd->sp_pwdp);
    //获取该用户名的密文密码
    char *encrypted_password = (char*)calloc(1024,sizeof(char));
    strncpy(encrypted_password,pspwd->sp_pwdp,strlen(pspwd->sp_pwdp));
    //printf("encrypted_password=%s\n",encrypted_password);
    //获取该用户的盐值
    char *salt = (char*)calloc(1024,sizeof(char));
    int length = strlen(pspwd->sp_pwdp);
    int i = length - 1;
    while(encrypted_password[i] != '$'){
        i--;
    }
    strncpy(salt,encrypted_password,i+1);
    //printf("salt=%s\n",salt);
    
    char encrypted_key[1024] = {0};
    strncpy(encrypted_key,crypt(key,salt),strlen(crypt(key,salt)));
    if(strncmp(encrypted_key,encrypted_password,length) != 0){
        return -1;
    }
    printf("登录成功!\n");
    strncpy(uname,name,strlen(name));
    free(name);
    free(salt);
    free(encrypted_password);
    return 0;
}




void * threadFunc(void * arg){
    PthreadPool * pp = (PthreadPool *)arg;
    printf("I'm ready!\n");
    while(1){

        pthread_mutex_lock(&pp->lock1);
        while(pp->rdy_que.size == 0){
            pthread_cond_wait(&pp->cond,&pp->lock1);
        }
        int netfd = deleteQueueNode(&pp->rdy_que);
        pthread_mutex_unlock(&pp->lock1);
       
////////////////////////////////////////////////////////////////////////
        
        Train train;
        char username[1024] = {0};
        int flag = 0;
        int epollfd = epoll_create(1);
        addEpollNode(epollfd,netfd);
        struct epoll_event readyset[5];
        while(1){
            epoll_wait(epollfd,readyset,5,-1);
            int rret1 = recv(readyset[0].data.fd,&train.length,sizeof(train.length),MSG_WAITALL);
            if(rret1 == 0){
                flag = 1;
                break;
            }
            recv(readyset[0].data.fd,train.data,train.length,MSG_WAITALL);
            char uname[1024] = {0};
            memcpy(uname,train.data,train.length);

            int rret2 = recv(readyset[0].data.fd,&train.length,sizeof(train.length),MSG_WAITALL);
            if(rret2 == 0){
                flag = 1;
                break;
            }
            recv(readyset[0].data.fd,train.data,train.length,MSG_WAITALL);
            char password[1024] = {0};
            memcpy(password,train.data,train.length);
            
            if(login(password,uname,username) == 0){
                send(readyset[0].data.fd,"1",1,MSG_NOSIGNAL);
                break;
            }
            send(readyset[0].data.fd,"0",1,MSG_NOSIGNAL);
        }
        if(flag == 1){
            printf("线程退出再监听\n");
            continue;
        }



               
        char * prev_path = (char *)calloc(1024,sizeof(char));
        char root[] = "../netdisk";
        char xiegang[] = "/";
        strcat(prev_path,root);
        strcat(prev_path,xiegang);
        strcat(prev_path,username);     //拼出前缀 ../netdisk/username
        printf("PthreadPool prev_path : %s\n",prev_path);
//////////////////////////////////////////////////////////////////////
        
               while(1){
            epoll_wait(epollfd,readyset,5,-1);
            int rret = recv(readyset[0].data.fd,&train.length,sizeof(train.length),MSG_WAITALL);
            if(rret == 0){
                printf("客户端断开连接\n");
                break;
            }
            recv(readyset[0].data.fd,train.data,train.length,MSG_WAITALL);
            char command[20] = {0};
            memcpy(command,train.data,train.length);
            printf("command:%s\n",command);
            if(strcmp(command,"cd") == 0){
                cdDispose(netfd,prev_path);
            }
            if(strcmp(command,"ls") == 0){
                lsDispose(netfd,prev_path);
            }
            if(strcmp(command,"puts") == 0){
                putsDispose(netfd,prev_path);
            }
            if(strcmp(command,"gets") == 0){
                printf("intogetsDispose\n");
                getsDispose(netfd,prev_path);
                printf("outgetsDispose\n");
            }
            if(strcmp(command,"mkdir") == 0){
                printf("intomkdirDispose\n");
                mkdirDispose(netfd,prev_path);
                printf("outmkdirDispose\n");
            }
            if(strcmp(command,"rmdir") == 0){
                printf("intormdirDispose\n");
                rmdirDispose(netfd,prev_path);
                printf("outrmdirDispose\n");
            }
            if(strcmp(command,"remove") == 0){
                rmfileDispose(netfd,prev_path);
            }
        }
        printf("线程退出再监听\n");
    }

    //  transfile(netfd);
        
    pthread_exit(NULL);
}


void createWorker(PthreadPool * pp,int size){
    for(int i = 0 ; i < size ; ++i){
        pthread_create(&pp->thd_arry.pid[i],NULL,threadFunc,pp);
    }
}


int pthreadPoolInit(PthreadPool * pp,int size){
    pthreadArryInit(&pp->thd_arry,size);
    readyQueueInit(&pp->rdy_que);
    pthread_mutex_init(&pp->lock1,NULL);
    pthread_mutex_init(&pp->lock2,NULL);
    pthread_cond_init(&pp->cond,NULL);
    createWorker(pp,size);
    return 0;
}
